﻿using NCS.IConnect.Core.Utilities;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Description;
using System.Web.Http.ModelBinding;

namespace NCS.IConnect.Core.WebApi.Description
{
    /// <summary>
    /// The request specific descriptor.
    /// </summary>
    public class RequestDescriptor
    {
        /// <summary>
        /// 
        /// </summary>
        public ApiDescriptorGenerator ApiDescriptorGenerator { get; internal set; }

        /// <summary>
        /// Gets the <see cref="ApiActionDescriptor"/>.
        /// </summary>
        /// <value>The <see cref="ApiActionDescriptor"/>.</value>
        [JsonIgnore]
        public ApiActionDescriptor ApiActionDescriptor { get; private set; }

        /// <summary>
        /// Gets the documentation provider.
        /// </summary>
        /// <value>The documentation provider.</value>
        [JsonIgnore]
        public IDocumentationProvider DocumentationProvider { get; private set; }

        /// <summary>
        /// Gets the request URI including query strings.
        /// </summary>
        /// <value>The request URI including query strings.</value>
        public string Uri { get; private set; }

        /// <summary>
        /// Gets the supported HTTP methods.
        /// </summary>
        /// <value>The supported HTTP methods.</value>
        public string SupportedHttpMethod { get; private set; }

        /// <summary>
        /// Gets the URI parameter descriptors.
        /// </summary>
        /// <value>The URI parameter descriptors.</value>
        public UriParameterDescriptor[] UriParameterDescriptors { get; private set; }

        /// <summary>
        /// Gets the headers.
        /// </summary>
        /// <value>The headers.</value>
        public HttpHeaderDescriptor[] Headers { get; private set; }

        /// <summary>
        /// Gets the content.
        /// </summary>
        /// <value>The content.</value>
        public RequestContentDescriptor Content { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="RequestDescriptor"/> class.
        /// </summary>
        /// <param name="apiActionDescriptor">The API action descriptor.</param>
        public RequestDescriptor(ApiActionDescriptor apiActionDescriptor)
        {
            Guard.ArgumentNotNull(apiActionDescriptor, "apiActionDescriptor");
            this.ApiActionDescriptor = apiActionDescriptor;
            this.DocumentationProvider = apiActionDescriptor.DocumentationProvider;
            this.SupportedHttpMethod = apiActionDescriptor.HttpActionDescriptor.SupportedHttpMethods.First().ToString().ToUpper();
                        
            this.UriParameterDescriptors = GetUriParameters(apiActionDescriptor);
            this.Uri = this.GenerateUrl(this.UriParameterDescriptors);
            this.Content = this.GetContent(apiActionDescriptor.HttpActionDescriptor);
            this.Headers = this.GetHeaderDescriptors(apiActionDescriptor.HttpActionDescriptor);
        }
        /// <summary>
        /// Gets <see cref="UriParameterDescriptor"/>s.
        /// </summary>
        /// <param name="apiActionDescriptor">The API action descriptor.</param>
        /// <returns>A <see cref="UriParameterDescriptor"/> array.</returns>
        protected  UriParameterDescriptor[] GetUriParameters(ApiActionDescriptor apiActionDescriptor)
        {
            List<UriParameterDescriptor> uriParameterDescriptors = new List<UriParameterDescriptor>();
            HttpParameterBinding[] parameterBindings = apiActionDescriptor.HttpActionDescriptor.ActionBinding.ParameterBindings;
            var parameterDescriptors = apiActionDescriptor.HttpActionDescriptor.GetParameters();

            for (int i = 0; i < parameterDescriptors.Count; i++)
            {
                HttpParameterDescriptor httpParameterDescriptor = parameterDescriptors[i];
                ModelBinderAttribute modelBinderAttribute = httpParameterDescriptor.GetCustomAttributes<ModelBinderAttribute>().FirstOrDefault();
                if (modelBinderAttribute != null && modelBinderAttribute.BinderType == typeof(VersionNoModelBinder))
                {
                   continue;
                }
                HttpParameterBinding parameterBinding = parameterBindings[i];
                if (!parameterBinding.WillReadBody)
                {
                    UriParameterExporterAttribute attribute = httpParameterDescriptor.GetCustomAttributes<UriParameterExporterAttribute>().FirstOrDefault()
                        ?? httpParameterDescriptor.ParameterType.GetCustomAttributes(typeof(UriParameterExporterAttribute), true).OfType <UriParameterExporterAttribute>().FirstOrDefault();
                    if (null != attribute)
                    { 
                        IUriParameterExporter exporter = (IUriParameterExporter)Activator.CreateInstance(attribute.UriParameterExporterType);
                        uriParameterDescriptors.AddRange(exporter.GetParameters(this.ApiActionDescriptor,httpParameterDescriptor, (IModelDocumentationProvider)this.DocumentationProvider).ToArray());
                    }
                    else if (typeof(SearchCriteriaBase).IsAssignableFrom(httpParameterDescriptor.ParameterType))
                    {
                        IUriParameterExporter exporter = new SearchCriteriaUriParameterExporter();
                        uriParameterDescriptors.AddRange(exporter.GetParameters(this.ApiActionDescriptor, httpParameterDescriptor, (IModelDocumentationProvider)this.DocumentationProvider).ToArray());
                    }
                    else
                    {
                        uriParameterDescriptors.Add(new UriParameterDescriptor(apiActionDescriptor, parameterDescriptors[i]));
                    }
                }
            }
            return uriParameterDescriptors.ToArray();
        }

        /// <summary>
        /// Gets the request's <see cref="RequestContentDescriptor"/>.
        /// </summary>
        /// <param name="httpActionDescriptor">The HTTP action descriptor.</param>
        /// <returns>The request's <see cref="RequestContentDescriptor"/>..</returns>
        protected  RequestContentDescriptor GetContent(HttpActionDescriptor httpActionDescriptor)
        {
            HttpParameterBinding[] parameterBindings = httpActionDescriptor.ActionBinding.ParameterBindings;
            var parameterDescriptors = httpActionDescriptor.GetParameters();
            for (int i = 0; i < parameterDescriptors.Count; i++)
            {
                if(parameterBindings[i].WillReadBody)
                {
                    var supportedMediaTypes = httpActionDescriptor.Configuration.Formatters.SelectMany(f => f.SupportedMediaTypes).Select(m => m.MediaType);
                    return new RequestContentDescriptor(parameterDescriptors[i],type=>ApiDescriptorGenerator.GetSample(type, httpActionDescriptor), supportedMediaTypes.ToArray());
                }
            }
            return null;
        }

        /// <summary>
        /// Gets the header descriptors.
        /// </summary>
        /// <param name="httpActionDescriptor">The HTTP action descriptor.</param>
        /// <returns>HttpHeaderDescriptor[].</returns>
        protected  HttpHeaderDescriptor[] GetHeaderDescriptors(HttpActionDescriptor httpActionDescriptor)
        {
            Collection<HttpHeaderDescriptor> headers = new Collection<HttpHeaderDescriptor>();
            foreach (RequestHeaderAttribute attribute in httpActionDescriptor.GetCustomAttributes<RequestHeaderAttribute>())
            {
                headers.Add(attribute.CreateHeaderDescriptor());
            }
            return headers.ToArray();
        }

        /// <summary>
        /// Generate request URL including query string.
        /// </summary>
        /// <returns>Request URL including query string.</returns>
        protected string GenerateUrl(UriParameterDescriptor[] uriParameterDescriptors)
        {
            string url = HttpContext.Current.Request.Url.AbsoluteUri.ToLower()
                .Replace(HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.TrimStart('~').ToLower(), "");
            RoutePrefixAttribute routePrefixAttribute = this.ApiActionDescriptor.ControllerDescriptor.HttpControllerDescriptor.GetCustomAttributes<RoutePrefixAttribute>().FirstOrDefault();
            if (null != routePrefixAttribute)
            {
                url = "/"+ routePrefixAttribute.Prefix;
            }
            RouteAttribute routeAttribute = this.ApiActionDescriptor.HttpActionDescriptor.GetCustomAttributes<RouteAttribute>().First();
            url += "/" + routeAttribute.Template.ToLower();
            //url = url.TrimEnd('/') + "?";
            //foreach(var parameter in uriParameterDescriptors)
            //{
            //    if (!url.Contains(parameter.ParameterName.ToLower()))
            //    {
            //        string value = "{" + parameter.ParameterName.ToLower() + "}";
            //        url += string.Format("{0}={1}&", parameter.ParameterName.ToLower(), value);
            //    }
            //}
            return url.TrimEnd('/');
        }
    }
}